﻿namespace EdiApp.Behaviors
{
	using System.Windows.Input;
	using System.Windows;

	/// <summary>
	/// Source:
	/// http://stackoverflow.com/questions/1034374/drag-and-drop-in-mvvm-with-scatterview
	/// http://social.msdn.microsoft.com/Forums/de-DE/wpf/thread/21bed380-c485-44fb-8741-f9245524d0ae
	/// 
	/// Attached behaviour to implement the drop event via delegate command binding or routed commands.
	/// </summary>
	public static class DropFileCommand
	{
		// Field of attached ICommand property
		private static readonly DependencyProperty DropCommandProperty = DependencyProperty.RegisterAttached(
				"DropCommand",
				typeof(ICommand),
				typeof(DropFileCommand),
				new PropertyMetadata(null, OnDropCommandChange));

		/// <summary>
		/// Setter method of the attached DropCommand <seealso cref="ICommand"/> property
		/// </summary>
		/// <param name="source"></param>
		/// <param name="value"></param>
		public static void SetDropCommand(DependencyObject source, ICommand value)
		{
			source.SetValue(DropCommandProperty, value);
		}

		/// <summary>
		/// Getter method of the attached DropCommand <seealso cref="ICommand"/> property
		/// </summary>
		/// <param name="source"></param>
		/// <returns></returns>
		public static ICommand GetDropCommand(DependencyObject source)
		{
			return (ICommand)source.GetValue(DropCommandProperty);
		}

		/// <summary>
		/// This method is hooked in the definition of the <seealso cref="DropCommandProperty"/>.
		/// It is called whenever the attached property changes - in our case the event of binding
		/// and unbinding the property to a sink is what we are looking for.
		/// </summary>
		/// <param name="d"></param>
		/// <param name="e"></param>
		private static void OnDropCommandChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			UIElement uiElement = d as UIElement;	  // Remove the handler if it exist to avoid memory leaks
			uiElement.Drop -= UIElement_Drop;

			var command = e.NewValue as ICommand;
			if (command != null)
			{
				// the property is attached so we attach the Drop event handler
				uiElement.Drop += UIElement_Drop;
			}
		}

		/// <summary>
		/// This method is called when the Drop event occurs. The sender should be the control
		/// on which this behaviour is attached - so we convert the sender into a <seealso cref="UIElement"/>
		/// and receive the Command through the <seealso cref="GetDropCommand"/> getter listed above.
		/// 
		/// The <paramref name="e"/> parameter contains the standard <seealso cref="DragEventArgs"/> data,
		/// which is unpacked and reales upon the bound command.
		/// 
		/// This implementation supports binding of delegate commands and routed commands.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private static void UIElement_Drop(object sender, DragEventArgs e)
		{
			UIElement uiElement = sender as UIElement;

			// Sanity check just in case this was somehow send by something else
			if (uiElement == null)
				return;

			ICommand dropCommand = DropFileCommand.GetDropCommand(uiElement);

			// There may not be a command bound to this after all
			if (dropCommand == null)
				return;

			if (e.Data.GetDataPresent(DataFormats.FileDrop))
			{
				string[] droppedFilePaths =
				e.Data.GetData(DataFormats.FileDrop, true) as string[];

				foreach (string droppedFilePath in droppedFilePaths)
				{
					// Check whether this attached behaviour is bound to a RoutedCommand
					if (dropCommand is RoutedCommand)
					{
						// Execute the routed command
						(dropCommand as RoutedCommand).Execute(droppedFilePath, uiElement);
					}
					else
					{
						// Execute the Command as bound delegate
						dropCommand.Execute(droppedFilePath);
					}
				}
			}
		}
	}
}
